import utils from '@/utils'
import { useUserInfoStore } from '@/store/modules/user'
import { IMessageType } from '@/store/modules/interface/message'
import { useMessageStore } from '@/store/modules/message'
import SparkMD5 from 'spark-md5'
import router from '@/router'
import { reqMergeFile, reqUploadFile, reqVerifyHash } from '@/api/message'
import { baseRes } from '@/api/type'

const userInfoStore = useUserInfoStore()
const messageStore = useMessageStore()

// 内核
let THREAD_COUNT = navigator.hardwareConcurrency || 4

const route = router.currentRoute
export default function useUpload(proxy) {
	const chatType = route.value.query.type == 'friend' ? 'private' : 'group'
	const CHUNK_SIZE = 1024 * 1024
	// 文件切片并且计算hash值,而且使用webworker进行计算
	const cutFile = async (file: File, chunks: Blob[]) => {
		return new Promise((resolve) => {
			const worker = new Worker(new URL('./worker.js', import.meta.url), {
				type: 'module'
			})
			worker.postMessage({
				CHUNK_SIZE,
				chunks
			})

			worker.onmessage = (e) => {
				console.log(e.data, 'hash计算完成')
				resolve(e.data)
			}
			// 切片数量
			// const chunkCount = Math.ceil(file.size / CHUNK_SIZE)
			// // 每个线程分多少个切片
			// const threadChunkCount = Math.ceil(chunkCount / THREAD_COUNT)
			// console.log(THREAD_COUNT, '内核')
			// console.log('每个线程分的切片', threadChunkCount)
			// if (chunkCount <= THREAD_COUNT) {
			// 	THREAD_COUNT = chunkCount
			// }
			// for (let i = 0; i < THREAD_COUNT; i++) {
			// 	// 创建一个线程,并且分配任务
			// 	const worker = new Worker(new URL('./worker.js', import.meta.url), {
			// 		type: 'module'
			// 	})
			//
			// 	let end = (i + 1) * threadChunkCount
			// 	let start = i * threadChunkCount
			// 	console.log('开始,结束', start, end)
			// 	if (end > chunkCount) {
			// 		end = chunkCount
			// 	}
			//
			// 	worker.postMessage({
			// 		file, // 文件对象
			// 		CHUNK_SIZE, // 切片大小
			// 		start, // 开始切片索引
			// 		end, // 结束切片索引
			// 		chunks
			// 	})
			//
			// 	worker.onmessage = (e) => {
			// 		// console.log(e.data)
			// 		resolve(e.data)
			// 	}
			// }
		})
	}
	// 文件分片
	const createChunks = (file: File) => {
		const chunks = []
		for (let i = 0; i < file.size; i += CHUNK_SIZE) {
			chunks.push(file.slice(i, i + CHUNK_SIZE))
		}
		return chunks
	}

	// 计算文件hash值
	const calculateHash = async (chunks: Blob[]) => {
		return new Promise((resolve, reject) => {
			// 首位切片全部参与计算,其他切片只计算首尾中两个字节
			const targets: Blob[] = []

			chunks.forEach((item, index) => {
				if (index == 0 || index == chunks.length - 1) {
					targets.push(item)
				} else {
					// 首两个字节
					targets.push(item.slice(0, 2))
					// 中两个字节
					targets.push(item.slice(CHUNK_SIZE / 2, CHUNK_SIZE / 2 + 2))
					// 末两个字节
					targets.push(item.slice(CHUNK_SIZE - 2, CHUNK_SIZE))
				}
			})
			const spark = new SparkMD5.ArrayBuffer()
			const fileReader = new FileReader()

			// 把新分片的数据转换为blob对象
			const blobs = new Blob(targets)
			fileReader.readAsArrayBuffer(blobs)
			fileReader.onload = (e) => {
				spark.append((e.target as FileReader).result)
				// 返回拿到的hash值
				resolve(spark.end())
			}
		})
	}

	// 合并切片
	const mergeRequest = async (fileHash: string, fileName: string, rawFile) => {
		const data = {
			fileHash,
			fileName,
			size: CHUNK_SIZE
		}
		const res = await reqMergeFile(data, chatType)
		let replaceIndex: number | undefined
		messageStore.messageChatList.forEach((item, index) => {
			// uid每个文件的的唯一标识
			if (item.uid == rawFile.uid) {
				replaceIndex = index
			}
		})
		if (res && res.code == 200) {
			// 合并成功
			const data = messageStore.messageChatList[replaceIndex as number]
			const msgType = data.msgType
			data.loading = false
			let dataObj
			let url = res.url
			// 如果是文件类型
			if (msgType == 3) {
				dataObj = JSON.parse(data.message)
				dataObj.url = url
			} else if (msgType === 2) {
				// 视频类型
				const videoInfo = JSON.parse(data.message)
				videoInfo.url = res.url
				url = JSON.stringify(videoInfo)
			} else {
				// 图片类型
				// 获取图片比例
				const scale: number = await utils.imgScale(rawFile)
				const imgHeight = 200 / scale
				url = JSON.stringify({
					url: res.url as string,
					height: imgHeight
				})
				/**
				 * url:{
				 *     url:后端的图片地址,
				 *     hiehgt:图片高度
				 * }
				 */
			}

			// 更换一下data的message
			data.message = (msgType === 3 ? JSON.stringify(dataObj) : url) as string
			messageStore.updateMessageData(data, replaceIndex as number)

			// 发送者id
			const { _id } = userInfoStore.userInfo
			// 接收者id
			const toId = route.value.query.id

			proxy.socket.emit(`${chatType}_chat`, data, _id, toId)
		} else {
			utils.msg(res.msg, 'error')
			messageStore.delMessageData(replaceIndex as number)
		}
	}

	/**
	 *
	 * @param chunks 文件切片数组
	 * @param existChunks 已存在文件切片
	 * @param fileHash 文件hash值
	 * @param fileName 文件名
	 * @param rawFile 文件对象
	 */
	const uploadChunks = async (
		chunks: Blob[],
		existChunks: string[],
		fileHash: string,
		fileName: string,
		rawFile: any
	) => {
		// dataObj:[{fileHash:xxx,chunkHash:xxx,chunk}]
		const dataObj = chunks.map((chunk, index) => {
			// 返回需要上传的数据
			return {
				fileHash,
				chunkHash: fileHash + '-' + index,
				chunk
			}
		})

		// 每个切片都需要有formData对象
		// formDatas:[formData,formData,...]
		const formDatas = dataObj
			.filter((item) => !existChunks.includes(item.chunkHash))
			.map((item, index) => {
				const formData = new FormData()
				formData.append('fileHash', item.fileHash)
				formData.append('chunkHash', item.chunkHash)
				formData.append('chunk', item.chunk)
				return formData
			})
		console.log('发给后端的formDatas', formDatas)
		console.log(dataObj)
		// 控制一下并发数
		const max = 6
		const taskPool: any = []
		let num = 0
		for (let i = 0; i < formDatas.length; i++) {
			const task = reqUploadFile(formDatas[i], chatType)
			task.then((res) => {
				console.log(res)
				num++
				const fileObj = messageStore.messageChatList.filter((item) => {
					return item.uid == rawFile.uid
				})
				fileObj[0].progressNum = (Math.round((num / formDatas.length) * 100) / 100) * 100
				taskPool.splice(taskPool.findIndex((item: any) => item === task))
			})

			taskPool.push(task)

			// 请求池已经到达最大请求数,需要等待请求池中有完成的请求 一个就行
			if (taskPool.length == max) {
				await Promise.race(taskPool)
			}
		}

		// 等待池子里面所有请求都完毕
		await Promise.all(taskPool)
		// 通知后端合并切片
		mergeRequest(fileHash, fileName, rawFile)
	}

	const handleFileUpload = async (rawFile: any) => {
		const file: File = rawFile.file
		// 获取文件名
		const fileName = file.name
		// 获取文件后缀名
		const fileSuffixName = utils.getFileSuffixName(fileName)
		// 消息类型
		let msgType = 3
		let url = ''
		// 如果文件是图片/视频 图片1 视频2
		if (fileSuffixName == 'mp4' || fileSuffixName == 'mov') {
			// 获取视频第一帧
			const videoBase64 = await utils.getVideoCover(file)
			const videoInfo = {
				base64: videoBase64 as string, // 封面图
				url: '' // 视频地址
			}
			url = JSON.stringify(videoInfo)
			msgType = 2
		} else if (
			fileSuffixName == 'jpeg' ||
			fileSuffixName == 'png' ||
			fileSuffixName == 'gif' ||
			fileSuffixName == 'jpg'
		) {
			// 获取资源的base64
			url = (await utils.readerFile(file)) as string
			msgType = 1
		}

		const { _id, nick, imgUrl } = userInfoStore.userInfo
		// 文件信息
		const fileInfo = {
			fileName,
			size: file.size,
			fileSuffixName,
			url: ''
		}

		const data: IMessageType = {
			userId: {
				_id,
				nick,
				imgUrl
			},
			time: utils.formatTime(new Date()),
			msgType,
			// 3是文件消息
			message: msgType == 3 ? JSON.stringify(fileInfo) : url,
			loading: true,
			progressNum: 0,
			uid: file.uid
		}
		messageStore.pushMessageData(data)

		// 文件切片
		console.time('cutFile')
		const chunks = createChunks(file)
		console.log('文件切片', chunks)
		console.timeEnd('cutFile')
		// 计算文件hash值
		// const fileHash = await calculateHash(chunks)
		const fileHash = await cutFile(file, chunks)
		console.log('文件hash值', fileHash)
		// 文件hash校验 (文件秒传,在上传文件前 把hash告诉后端,如果后端有文件,直接返回上传成功)
		const reqData = {
			fileHash,
			fileName
		}
		const reqType = route.value.query.type == 'friend' ? 'private' : 'group'
		const res: baseRes = await reqVerifyHash(reqData, reqType)
		console.log('后端校验文件hash值', res)
		/**
		 * res:{
		 *     code,
		 *     data:{
		 *         shouldUpload, // 后端没有该文件
		 *         existChunks   // 后端有该文件,已经上传的分片
		 *     }
		 *     }
		 * }
		 */
		if (res.data.shouldUpload) {
			console.log('后端没有该文件')
			// 后端没有该文件,上传分片
			uploadChunks(chunks, res.data.existChunks, fileHash as string, fileName, file)
		} else {
			// 后端有该文件
			let replaceIndex: number | undefined
			messageStore.messageChatList.forEach((item, index) => {
				// uid 上传每张图片的唯一标识
				if (item.uid === rawFile.file.uid) {
					replaceIndex = index
				}
			})
			const data = messageStore.messageChatList[replaceIndex as number]
			const msgType = data.msgType
			data.loading = false
			data.progressNum = 100
			let dataObj
			let url = res.data.url
			if (msgType === 3) {
				dataObj = JSON.parse(data.message)
				dataObj.url = url
			} else if (msgType === 2) {
				const videoInfo = JSON.parse(data.message)
				videoInfo.url = res.data.url
				url = JSON.stringify(videoInfo)
			} else {
				const scale = await utils.imgScale(rawFile.file as File)
				const imgHeight = 200 / scale
				url = JSON.stringify({
					url: res.data.url as string,
					height: imgHeight
				})
			}
			data.message = (msgType === 3 ? JSON.stringify(dataObj) : url) as string
			messageStore.updateMessageData(data, replaceIndex as number)
			// 发送者id
			const { _id } = userInfoStore.userInfo
			// 接收者id
			const toId = route.value.query.id
			const type = route.value.query.type
			proxy.socket.emit(`${type === 'friend' ? 'private' : 'group'}_chat`, data, _id, toId)
		}

		// 上传分片
	}

	const handleUploadError = (error: any) => {
		console.log(error)
	}
	const handleExceed = (exceeded: any) => {
		console.log(exceeded)
	}

	return {
		handleFileUpload,
		handleUploadError,
		handleExceed
	}
}
